﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace Effects.Effects.GuiParts
{
    /// <summary>
    ///  Simple control derived from Button that changes state with every click.These states change in cirlce.
    /// </summary>
    /// <remarks>
    /// <para>
    ///  Class represents switch that can have from 1 to N states. It's state changed when user clicks and OnSwitch event is raised.
    ///  </para>
    /// <para> 
    ///  Graphically it is represented as rectangle, with border and smaller rectangle (called switch or switchKnob)
    ///  </para>
    /// </remarks>
    public class Switch:Button
    {
        /// <summary>
        ///  Constant storing min size of smaller rectangle (switch)
        /// </summary>
        protected const int minSwitchSide = 5;

        /// <summary>
        ///  Constant for distance between broder and switch
        /// </summary>
        protected const int switchDistance = 1;

        /// <summary>
        ///  Width of the border.
        /// </summary>
        protected const int borderWidth = 1;

        /// <summary>
        ///  Overriden MinimumSize property. Width of minimum size is minSwitchSide + 2* switchDistance + 2*borderWidth (for borders and spaces on both sides).
        ///  Height of minimum size is stateCount * minSwitchSide + 2* switchDistance + 2*borderWidth 
        /// </summary>
        public override Size MinimumSize
        {
            get
            {
                return new Size(minSwitchSide+2*switchDistance+2*borderWidth, stateCount * minSwitchSide + 2 * switchDistance + 2 * borderWidth);
            }
            set
            {
                base.MinimumSize = value;
            }
        }

        #region state
        // state count and switching

        /// <summary>
        ///  Current state of the switch. It changes with every click.
        /// </summary>
        protected int state;
        /// <summary>
        ///  Gets or sets current state of the switch
        /// </summary>
        public int State
        {
            get { return state; }
            set
            {
                if (value > -1 && value < stateCount)
                {
                    state = value;
                    Render();
                    RefreshMultiThread();
                }
            }
        }
        
        // this value can't be changed
        /// <summary>
        ///  States count. How many states has the switch.
        /// </summary>
        protected int stateCount;
        /// <summary>
        ///  Gets the count of switch's states.
        /// </summary>
        public int StateCount
        {
            get{return stateCount;}
        }

        #endregion

        /// <summary>
        ///  Creates switch with given number of states.
        /// </summary>
        /// <param name="states"> State count. Tells how many states will the swich have.</param>
        public Switch(int states)
        {
            this.stateCount = states;
            // seting basic switch knob color
            switchKnobColor = Color.Firebrick;
            switchKnobBrush = new SolidBrush(Color.Firebrick);

            refreshInvoker = new MethodInvoker(Refresh);

            PrepareSize();
            PrepareBackground();

            Render();
        }

        /// <summary>
        ///  Size of the smaller rectangle (switch, switchKnob)
        /// </summary>
        protected Size switchKnob;

        // method controls actual size, and adjusts it if needed
        /// <summary>
        ///  Checks current size. Adjusts it if needed (if it is smaller than MinimumSize, or so)
        /// </summary>
        protected void PrepareSize()
        {
            if(this.Width < MinimumSize.Width)
                this.Width=MinimumSize.Width;
            if (this.Height < MinimumSize.Height)
                this.Height = MinimumSize.Height;

            // preparing switchKnob
            switchKnob=new Size(this.Width-2*borderWidth-2*switchDistance,Convert.ToInt32( Math.Floor( ((double) (this.Height-2*borderWidth-2*switchDistance)) / stateCount) ) );
            PrepareBackground();
        }

        // overriden on resize, calls PrepareSize, that can change size, if it was too small
        /// <summary>
        ///  Overridden OnResize method. Calls PrepareSize, that can change size, if it was too small.
        /// </summary>
        /// <param name="e"> Resize event arguments </param>
        protected override void OnResize(EventArgs e)
        {
            PrepareSize();
            Render();
            //base.OnResize(e);
        }

        
        /// <summary>
        ///  Color of the smaller rectangle (switchKnob)
        /// </summary>
        protected Color switchKnobColor;
        
        /// <summary>
        ///  SolidBrush used for painting switchKnob. It has the same color as switchKnobColor.
        /// </summary>
        protected SolidBrush switchKnobBrush;
        /// <summary>
        ///  Gets or sets the color of switch Knob.
        /// </summary>
        public Color SwitchKnobColor
        {
            get { return switchKnobColor; }
            set 
            { 
                switchKnobColor = value;
                switchKnobBrush = new SolidBrush(switchKnobColor);
                Render();
            }
        }

        #region painting_control
        // rendering and painting control

        /// <summary>
        ///  Represents background
        /// </summary>
        /// <remarks>
        ///  This member is used to speed the rendering a bit up. There is no need to render background every time just when size of the switch changes.
        ///  On state change Render method just paints switch Knob
        /// </remarks>
        protected Bitmap background;

        /// <summary>
        ///  Creates background
        /// </summary>
        protected void PrepareBackground()
        {
            Bitmap newBackground = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(newBackground);

            g.FillRectangle(Brushes.DarkGray, 0, 0, this.Width, this.Height);
            g.DrawLine(Pens.Black, 0, 0, this.Width-borderWidth, 0);
            g.DrawLine(Pens.Black, this.Width - borderWidth, 0, this.Width - borderWidth, this.Height - borderWidth);
            g.DrawLine(Pens.Black, this.Width - borderWidth, this.Height - borderWidth, 0, this.Height - borderWidth);
            g.DrawLine(Pens.Black, 0, this.Height - borderWidth, 0, 0);

            background = newBackground;
        }

        /// <summary>
        /// Bitmap used for double buffer
        /// </summary>
        protected Bitmap image;

        /// <summary>
        ///  Renders image. Takes background and paints switch Knob.
        /// </summary>
        protected void Render()
        {
            Bitmap newImage = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(newImage);

            g.DrawImage(background, 0, 0);
            g.FillRectangle(switchKnobBrush, borderWidth + switchDistance, borderWidth + switchDistance + (state * switchKnob.Height), switchKnob.Width, switchKnob.Height);

            image = newImage;
        }

        /// <summary>
        ///  overridden OnPaint method. Draws image.
        /// </summary>
        /// <param name="pevent"> Painting event arguments. </param>
        protected override void OnPaint(PaintEventArgs pevent)
        {
            //base.OnPaint(pevent);
            Graphics g = pevent.Graphics;
            g.DrawImage(image, 0, 0);
        }
        #endregion

        #region main work
        // reacting on user's clicks

        /// <summary>
        ///  Overridden OnClick method. Now it changes state and forces Render and Refresh, and calls OnSwitch event
        /// </summary>
        protected override void OnClick(EventArgs e)
        {
            base.OnClick(e);
            state++;
            if (state == stateCount)
                state = 0;

            this.Text=state.ToString();
            Render();
            Refresh();

            try
            {
                this.OnSwitch(this,new SwitchEventArgs(state));
            }
            catch(NullReferenceException)
            {}
        }

        /// <summary>
        ///  Event is raised everytime user clicks the control and changes the state of the switch
        /// </summary>
        public event SwitchSwitched OnSwitch;

        #endregion

        #region refresh_multi_thread
        // refreh control from all threads

        /// <summary>
        ///  Method invoker for refresh method
        /// </summary>
        private MethodInvoker refreshInvoker;

        /// <summary>
        ///  Forces control to redraw. Can be called from not GUI threads.
        /// </summary>
        private void RefreshMultiThread()
        {
            if (InvokeRequired)
                BeginInvoke(refreshInvoker);
            else
                Refresh();
        }

        #endregion
    }

   
}
